﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tempest.Messages;

namespace TempestChessComputer
{
    class Board
    {
        GameType gameType;
        
        //stores the location of stationary pieces
        public Piece[,] PieceGrid
        {
            get;
            set;
        }

        //stores the target square of moving pieces
        public Piece[,] MovingPieceGrid
        {
            get;
            set;
        }

        public Dictionary<int, Piece> PiecesById
        {
            get;
            set;
        }

        public List<Piece> PiecesInHand
        {
            get;
            set;
        }

        bool IsFourWay
        {
            get;
            set;
        }

        public int BoardWidth
        {
            get;
            set;
        }

        public int MoveSpeed
        {
            get;
            set;
        }

        public int DelayDuration
        {
            get;
            set;
        }
       
        public Board(GameType gameType, int moveSpeed, int delayDuration)
        {
            PiecesById = new Dictionary<int, Piece>();
            PiecesInHand = new List<Piece>();
            this.gameType = gameType;
            switch (gameType)
            {
                case GameType.STANDARD:                    
                case GameType.FASTSTANDARD:
                case GameType.CRAZYHOUSE:
                    initializeStandard();
                    break;
                case GameType.FASTFOURWAY:
                case GameType.FOURWAYCRAZYHOUSE:                    
                    initializeFourWay();
                    break;                
                case GameType.BUGHOUSE:                    
                    initializeBugHouse();
                    break;                                            
            }
            MoveSpeed = moveSpeed;
            DelayDuration = delayDuration;
        }        

        //returns the piece that is either at or moving to the specified coordinates, or null if no such piece
        public Piece getPieceAtOrMovingTo(int x, int y)
        {
            Piece p1 = PieceGrid[x, y];
            Piece p2 = MovingPieceGrid[x, y];

            if (p1 != null)
                return p1;
            else
                return p2;
        }

        public bool isLegalSquare(int x, int y)
        {
            if (BoardWidth == 8)
                return (x >= 0 && x < 8) && (y >= 0 && y < 8);
            else
            {
                if (isDeadSquare(x, y))
                    return false;
                else
                {
                    return (x >= 0 && x < 12) && (y >= 0 && y < 12);
                }
            }
        }

        //is the square a dead square on a fast four way board?
        private bool isDeadSquare(int x, int y)
        {
            if ((x == 0 && y == 0) ||
                (x == 0 && y == 1) ||
                (x == 0 && y == 10) ||
                (x == 0 && y == 11) ||
                (x == 1 && y == 0) ||
                (x == 1 && y == 1) ||
                (x == 1 && y == 10) ||
                (x == 1 && y == 11) ||
                (x == 10 && y == 0) ||
                (x == 10 && y == 1) ||
                (x == 10 && y == 10) ||
                (x == 10 && y == 11) ||
                (x == 11 && y == 0) ||
                (x == 11 && y == 1) ||
                (x == 11 && y == 10) ||
                (x == 11 && y == 11))
                return true;
            else
                return false;

        }

        //returns true if the specified move will result in the piece getting inmove captured, false otherwise
        //this is a fairly expensive check, so I suggest you use it only to invalidate potential moves you consider strong
        //todo: take teamConfiguration into account
        public bool isInmoveCaptured(Move m, long time)
        {
            if(PiecesById.ContainsKey(m.PieceId))
            {
                Piece p = PiecesById[m.PieceId];
                if (p.Type != PieceType.KNIGHT)   //knights cannot inmove capture or be inmove captured
                {
                    //each element of movePath stores the time at which the piece would reach that square if it made the Move m
                    long[,] movePath = new long[this.BoardWidth, this.BoardWidth];
                    int xDirection = p.X < m.TargetX ? 1 : (p.X == m.TargetX ? 0 : -1);
                    int yDirection = p.Y < m.TargetY ? 1 : (p.Y == m.TargetY ? 0 : -1);
                    int x = p.X;
                    int y = p.Y;                    
                    for(int distance = 0; x != m.TargetX || y != m.TargetY; distance++)
                    {
                        x += xDirection;
                        y += yDirection;
                        movePath[x, y] = time + distance * MoveSpeed;                        
                    }

                    foreach (Piece sweeper in PiecesById.Values)
                    {
                        if (sweeper.IsMoving && sweeper.Player != p.Player && sweeper.Type != PieceType.KNIGHT)
                        {
                            //sweeper is a moving enemy piece. We now need to check if its move path intersects the path of Move m
                            //and if the pieces reach the intersection at the same time
                            xDirection = sweeper.X < sweeper.TargetX ? 1 : (sweeper.X == sweeper.TargetX ? 0 : -1);
                            yDirection = sweeper.Y < sweeper.TargetY ? 1 : (sweeper.Y == sweeper.TargetY ? 0 : -1);
                            long elapsedTime = time - sweeper.MoveStartTime;
                            int distance = (int)elapsedTime / MoveSpeed;
                            x = sweeper.X + xDirection * distance;
                            y = sweeper.Y + yDirection * distance;
                            for (; x != sweeper.TargetX || y != sweeper.TargetY; distance++)
                            {
                                x += xDirection;
                                y += yDirection;
                                long timeToSquare = sweeper.MoveStartTime + distance * MoveSpeed;
                                if (movePath[x, y] != 0)
                                {                                    
                                    //check time of intersection
                                    if (movePath[x, y] >= timeToSquare && movePath[x, y] <= (timeToSquare + MoveSpeed))
                                    {
                                        return true;
                                    }
                                    if (movePath[x, y] + MoveSpeed >= timeToSquare && movePath[x, y] + MoveSpeed <= (timeToSquare + MoveSpeed))
                                    {
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            return false;
        }

        public void update(long time)
        {
            //update moving/delayed pieces and refresh the piece grid/board state
            PieceGrid = new Piece[BoardWidth, BoardWidth];
            MovingPieceGrid = new Piece[BoardWidth, BoardWidth];
            foreach (Piece p in PiecesById.Values)
            {
                if (p.IsMoving)
                {
                    if (time >= p.MoveDelayStart)
                    {
                        p.IsMoving = false;
                        p.X = p.TargetX;
                        p.Y = p.TargetY;
                        p.IsDelayed = true;

                        //handle pawn promotion
                        if (p.Type == PieceType.PAWN)
                        {
                            if (p.Player == 0 && p.Y == BoardWidth - 1)
                                p.Type = PieceType.QUEEN;
                            else if (p.Player == 1 && p.Y == 0)
                                p.Type = PieceType.QUEEN;
                            else if (p.Player == 2 && p.X == BoardWidth - 1)
                                p.Type = PieceType.QUEEN;
                            else if (p.Player == 3 && p.X == 0)
                                p.Type = PieceType.QUEEN;
                        }
                    }
                    else
                    {
                        MovingPieceGrid[p.TargetX, p.TargetY] = p;
                    }
                }
                if (p.IsDelayed)
                {
                    if (time >= p.MoveDelayFinishedTime)
                    {
                        p.IsDelayed = false;
                        p.FirstMove = false;
                    }
                }

                if (!p.IsMoving)
                {
                    PieceGrid[p.X, p.Y] = p;
                }


            }
        }

        private void initializeStandard()
        {
            IsFourWay = false;
            BoardWidth = 8;
            PieceGrid = new Piece[8, 8];
            MovingPieceGrid = new Piece[8, 8];            

            for (int i = 0; i < 8; i++)
            {
                Piece whitePawn = new Piece(PieceType.PAWN, this, i, 0);
                whitePawn.X = i;
                whitePawn.Y = 1;
                PieceGrid[whitePawn.X, whitePawn.Y] = whitePawn;
                PiecesById.Add(whitePawn.PieceId, whitePawn);

                Piece blackPawn = new Piece(PieceType.PAWN, this, 8 + i, 1);
                blackPawn.X = i;
                blackPawn.Y = 6;
                PieceGrid[blackPawn.X, blackPawn.Y] = blackPawn;
                PiecesById.Add(blackPawn.PieceId, blackPawn);
            }

            Piece whiteRook = new Piece(PieceType.ROOK, this, 16, 0);
            whiteRook.X = 0;
            whiteRook.Y = 0;
            PieceGrid[whiteRook.X, whiteRook.Y] = whiteRook;
            PiecesById.Add(whiteRook.PieceId, whiteRook);

            Piece whiteKnight = new Piece(PieceType.KNIGHT, this, 17, 0);
            whiteKnight.X = 1;
            whiteKnight.Y = 0;
            PieceGrid[whiteKnight.X, whiteKnight.Y] = whiteKnight;
            PiecesById.Add(whiteKnight.PieceId, whiteKnight);

            Piece whiteBishop = new Piece(PieceType.BISHOP, this, 18, 0);
            whiteBishop.X = 2;
            whiteBishop.Y = 0;
            PieceGrid[whiteBishop.X, whiteBishop.Y] = whiteBishop;
            PiecesById.Add(whiteBishop.PieceId, whiteBishop);

            Piece whiteQueen = new Piece(PieceType.QUEEN, this, 19, 0);
            whiteQueen.X = 3;
            whiteQueen.Y = 0;
            PieceGrid[whiteQueen.X, whiteQueen.Y] = whiteQueen;
            PiecesById.Add(whiteQueen.PieceId, whiteQueen);

            Piece whiteKing = new Piece(PieceType.KING, this, 20, 0);
            whiteKing.X = 4;
            whiteKing.Y = 0;
            PieceGrid[whiteKing.X, whiteKing.Y] = whiteKing;
            PiecesById.Add(whiteKing.PieceId, whiteKing);

            Piece whiteBishop2 = new Piece(PieceType.BISHOP, this, 21, 0);
            whiteBishop2.X = 5;
            whiteBishop2.Y = 0;
            PieceGrid[whiteBishop2.X, whiteBishop2.Y] = whiteBishop2;
            PiecesById.Add(whiteBishop2.PieceId, whiteBishop2);

            Piece whiteKnight2 = new Piece(PieceType.KNIGHT, this, 22, 0);
            whiteKnight2.X = 6;
            whiteKnight2.Y = 0;
            PieceGrid[whiteKnight2.X, whiteKnight2.Y] = whiteKnight2;
            PiecesById.Add(whiteKnight2.PieceId, whiteKnight2);

            Piece whiteRook2 = new Piece(PieceType.ROOK, this, 23, 0);
            whiteRook2.X = 7;
            whiteRook2.Y = 0;
            PieceGrid[whiteRook2.X, whiteRook2.Y] = whiteRook2;
            PiecesById.Add(whiteRook2.PieceId, whiteRook2);

            Piece blackRook = new Piece(PieceType.ROOK, this, 24, 1);
            blackRook.X = 0;
            blackRook.Y = 7;
            PieceGrid[blackRook.X, blackRook.Y] = blackRook;
            PiecesById.Add(blackRook.PieceId, blackRook);

            Piece blackKnight = new Piece(PieceType.KNIGHT, this, 25, 1);
            blackKnight.X = 1;
            blackKnight.Y = 7;
            PieceGrid[blackKnight.X, blackKnight.Y] = blackKnight;
            PiecesById.Add(blackKnight.PieceId, blackKnight);

            Piece blackBishop = new Piece(PieceType.BISHOP, this, 26, 1);
            blackBishop.X = 2;
            blackBishop.Y = 7;
            PieceGrid[blackBishop.X, blackBishop.Y] = blackBishop;
            PiecesById.Add(blackBishop.PieceId, blackBishop);

            Piece blackQueen = new Piece(PieceType.QUEEN, this, 27, 1);
            blackQueen.X = 3;
            blackQueen.Y = 7;
            PieceGrid[blackQueen.X, blackQueen.Y] = blackQueen;
            PiecesById.Add(blackQueen.PieceId, blackQueen);

            Piece blackKing = new Piece(PieceType.KING, this, 28, 1);
            blackKing.X = 4;
            blackKing.Y = 7;
            PieceGrid[blackKing.X, blackKing.Y] = blackKing;
            PiecesById.Add(blackKing.PieceId, blackKing);

            Piece blackBishop2 = new Piece(PieceType.BISHOP, this, 29, 1);
            blackBishop2.X = 5;
            blackBishop2.Y = 7;
            PieceGrid[blackBishop2.X, blackBishop2.Y] = blackBishop2;
            PiecesById.Add(blackBishop2.PieceId, blackBishop2);

            Piece blackKnight2 = new Piece(PieceType.KNIGHT, this, 30, 1);
            blackKnight2.X = 6;
            blackKnight2.Y = 7;
            PieceGrid[blackKnight2.X, blackKnight2.Y] = blackKnight2;
            PiecesById.Add(blackKnight2.PieceId, blackKnight2);

            Piece blackRook2 = new Piece(PieceType.ROOK, this, 31, 1);
            blackRook2.X = 7;
            blackRook2.Y = 7;
            PieceGrid[blackRook2.X, blackRook2.Y] = blackRook2;
            PiecesById.Add(blackRook2.PieceId, blackRook2);
        }

        private void initializeFourWay()
        {
            IsFourWay = true;
            BoardWidth = 12;
            PieceGrid = new Piece[12, 12];
            MovingPieceGrid = new Piece[12, 12];                      

            for (int i = 0; i < 8; i++)
            {
                Piece whitePawn = new Piece(PieceType.PAWN, this, i, 0);
                whitePawn.X = 2 + i;
                whitePawn.Y = 1;
                PieceGrid[whitePawn.X, whitePawn.Y] = whitePawn;
                PiecesById.Add(whitePawn.PieceId, whitePawn);

                Piece blackPawn = new Piece(PieceType.PAWN, this, 8 + i, 1);
                blackPawn.X = 2 + i;
                blackPawn.Y = 10;
                PieceGrid[blackPawn.X, blackPawn.Y] = blackPawn;
                PiecesById.Add(blackPawn.PieceId, blackPawn);

                Piece greenPawn = new Piece(PieceType.PAWN, this, 16 + i, 2);
                greenPawn.X = 1;
                greenPawn.Y = 2 + i;
                PieceGrid[greenPawn.X, greenPawn.Y] = greenPawn;
                PiecesById.Add(greenPawn.PieceId, greenPawn);

                Piece redPawn = new Piece(PieceType.PAWN, this, 24 + i, 3);
                redPawn.X = 10;
                redPawn.Y = 2 + i;
                PieceGrid[redPawn.X, redPawn.Y] = redPawn;
                PiecesById.Add(redPawn.PieceId, redPawn);
            }

            Piece whiteRook = new Piece(PieceType.ROOK, this, 32, 0);
            whiteRook.X = 2;
            whiteRook.Y = 0;
            PieceGrid[whiteRook.X, whiteRook.Y] = whiteRook;
            PiecesById.Add(whiteRook.PieceId, whiteRook);

            Piece whiteKnight = new Piece(PieceType.KNIGHT, this, 33, 0);
            whiteKnight.X = 3;
            whiteKnight.Y = 0;
            PieceGrid[whiteKnight.X, whiteKnight.Y] = whiteKnight;
            PiecesById.Add(whiteKnight.PieceId, whiteKnight);

            Piece whiteBishop = new Piece(PieceType.BISHOP, this, 34, 0);
            whiteBishop.X = 4;
            whiteBishop.Y = 0;
            PieceGrid[whiteBishop.X, whiteBishop.Y] = whiteBishop;
            PiecesById.Add(whiteBishop.PieceId, whiteBishop);

            Piece whiteQueen = new Piece(PieceType.QUEEN, this, 35, 0);
            whiteQueen.X = 5;
            whiteQueen.Y = 0;
            PieceGrid[whiteQueen.X, whiteQueen.Y] = whiteQueen;
            PiecesById.Add(whiteQueen.PieceId, whiteQueen);

            Piece whiteKing = new Piece(PieceType.KING, this, 36, 0);
            whiteKing.X = 6;
            whiteKing.Y = 0;
            PieceGrid[whiteKing.X, whiteKing.Y] = whiteKing;
            PiecesById.Add(whiteKing.PieceId, whiteKing);

            Piece whiteBishop2 = new Piece(PieceType.BISHOP, this, 37, 0);
            whiteBishop2.X = 7;
            whiteBishop2.Y = 0;
            PieceGrid[whiteBishop2.X, whiteBishop2.Y] = whiteBishop2;
            PiecesById.Add(whiteBishop2.PieceId, whiteBishop2);

            Piece whiteKnight2 = new Piece(PieceType.KNIGHT, this, 38, 0);
            whiteKnight2.X = 8;
            whiteKnight2.Y = 0;
            PieceGrid[whiteKnight2.X, whiteKnight2.Y] = whiteKnight2;
            PiecesById.Add(whiteKnight2.PieceId, whiteKnight2);

            Piece whiteRook2 = new Piece(PieceType.ROOK, this, 39, 0);
            whiteRook2.X = 9;
            whiteRook2.Y = 0;
            PieceGrid[whiteRook2.X, whiteRook2.Y] = whiteRook2;
            PiecesById.Add(whiteRook2.PieceId, whiteRook2);

            Piece blackRook = new Piece(PieceType.ROOK, this, 40, 1);
            blackRook.X = 2;
            blackRook.Y = 11;
            PieceGrid[blackRook.X, blackRook.Y] = blackRook;
            PiecesById.Add(blackRook.PieceId, blackRook);

            Piece blackKnight = new Piece(PieceType.KNIGHT, this, 41, 1);
            blackKnight.X = 3;
            blackKnight.Y = 11;
            PieceGrid[blackKnight.X, blackKnight.Y] = blackKnight;
            PiecesById.Add(blackKnight.PieceId, blackKnight);

            Piece blackBishop = new Piece(PieceType.BISHOP, this, 42, 1);
            blackBishop.X = 4;
            blackBishop.Y = 11;
            PieceGrid[blackBishop.X, blackBishop.Y] = blackBishop;
            PiecesById.Add(blackBishop.PieceId, blackBishop);

            Piece blackQueen = new Piece(PieceType.QUEEN, this, 43, 1);
            blackQueen.X = 6;
            blackQueen.Y = 11;
            PieceGrid[blackQueen.X, blackQueen.Y] = blackQueen;
            PiecesById.Add(blackQueen.PieceId, blackQueen);

            Piece blackKing = new Piece(PieceType.KING, this, 44, 1);
            blackKing.X = 5;
            blackKing.Y = 11;
            PieceGrid[blackKing.X, blackKing.Y] = blackKing;
            PiecesById.Add(blackKing.PieceId, blackKing);

            Piece blackBishop2 = new Piece(PieceType.BISHOP, this, 45, 1);
            blackBishop2.X = 7;
            blackBishop2.Y = 11;
            PieceGrid[blackBishop2.X, blackBishop2.Y] = blackBishop2;
            PiecesById.Add(blackBishop2.PieceId, blackBishop2);

            Piece blackKnight2 = new Piece(PieceType.KNIGHT, this, 46, 1);
            blackKnight2.X = 8;
            blackKnight2.Y = 11;
            PieceGrid[blackKnight2.X, blackKnight2.Y] = blackKnight2;
            PiecesById.Add(blackKnight2.PieceId, blackKnight2);

            Piece blackRook2 = new Piece(PieceType.ROOK, this, 47, 1);
            blackRook2.X = 9;
            blackRook2.Y = 11;
            PieceGrid[blackRook2.X, blackRook2.Y] = blackRook2;
            PiecesById.Add(blackRook2.PieceId, blackRook2);

            Piece greenRook = new Piece(PieceType.ROOK, this, 48, 2);
            greenRook.X = 0;
            greenRook.Y = 2;
            PieceGrid[greenRook.X, greenRook.Y] = greenRook;
            PiecesById.Add(greenRook.PieceId, greenRook);

            Piece greenKnight = new Piece(PieceType.KNIGHT, this, 49, 2);
            greenKnight.X = 0;
            greenKnight.Y = 3;
            PieceGrid[greenKnight.X, greenKnight.Y] = greenKnight;
            PiecesById.Add(greenKnight.PieceId, greenKnight);

            Piece greenBishop = new Piece(PieceType.BISHOP, this, 50, 2);
            greenBishop.X = 0;
            greenBishop.Y = 4;
            PieceGrid[greenBishop.X, greenBishop.Y] = greenBishop;
            PiecesById.Add(greenBishop.PieceId, greenBishop);

            Piece greenQueen = new Piece(PieceType.QUEEN, this, 51, 2);
            greenQueen.X = 0;
            greenQueen.Y = 5;
            PieceGrid[greenQueen.X, greenQueen.Y] = greenQueen;
            PiecesById.Add(greenQueen.PieceId, greenQueen);

            Piece greenKing = new Piece(PieceType.KING, this, 52, 2);
            greenKing.X = 0;
            greenKing.Y = 6;
            PieceGrid[greenKing.X, greenKing.Y] = greenKing;
            PiecesById.Add(greenKing.PieceId, greenKing);

            Piece greenBishop2 = new Piece(PieceType.BISHOP, this, 53, 2);
            greenBishop2.X = 0;
            greenBishop2.Y = 7;
            PieceGrid[greenBishop2.X, greenBishop2.Y] = greenBishop2;
            PiecesById.Add(greenBishop2.PieceId, greenBishop2);

            Piece greenKnight2 = new Piece(PieceType.KNIGHT, this, 54, 2);
            greenKnight2.X = 0;
            greenKnight2.Y = 8;
            PieceGrid[greenKnight2.X, greenKnight2.Y] = greenKnight2;
            PiecesById.Add(greenKnight2.PieceId, greenKnight2);

            Piece greenRook2 = new Piece(PieceType.ROOK, this, 55, 2);
            greenRook2.X = 0;
            greenRook2.Y = 9;
            PieceGrid[greenRook2.X, greenRook2.Y] = greenRook2;
            PiecesById.Add(greenRook2.PieceId, greenRook2);

            Piece redRook = new Piece(PieceType.ROOK, this, 56, 3);
            redRook.X = 11;
            redRook.Y = 2;
            PieceGrid[redRook.X, redRook.Y] = redRook;
            PiecesById.Add(redRook.PieceId, redRook);

            Piece redKnight = new Piece(PieceType.KNIGHT, this, 57, 3);
            redKnight.X = 11;
            redKnight.Y = 3;
            PieceGrid[redKnight.X, redKnight.Y] = redKnight;
            PiecesById.Add(redKnight.PieceId, redKnight);

            Piece redBishop = new Piece(PieceType.BISHOP, this, 58, 3);
            redBishop.X = 11;
            redBishop.Y = 4;
            PieceGrid[redBishop.X, redBishop.Y] = redBishop;
            PiecesById.Add(redBishop.PieceId, redBishop);

            Piece redKing = new Piece(PieceType.KING, this, 59, 3);
            redKing.X = 11;
            redKing.Y = 5;
            PieceGrid[redKing.X, redKing.Y] = redKing;
            PiecesById.Add(redKing.PieceId, redKing);

            Piece redQueen = new Piece(PieceType.QUEEN, this, 60, 3);
            redQueen.X = 11;
            redQueen.Y = 6;
            PieceGrid[redQueen.X, redQueen.Y] = redQueen;
            PiecesById.Add(redQueen.PieceId, redQueen);

            Piece redBishop2 = new Piece(PieceType.BISHOP, this, 61, 3);
            redBishop2.X = 11;
            redBishop2.Y = 7;
            PieceGrid[redBishop2.X, redBishop2.Y] = redBishop2;
            PiecesById.Add(redBishop2.PieceId, redBishop2);

            Piece redKnight2 = new Piece(PieceType.KNIGHT, this, 62, 3);
            redKnight2.X = 11;
            redKnight2.Y = 8;
            PieceGrid[redKnight2.X, redKnight2.Y] = redKnight2;
            PiecesById.Add(redKnight2.PieceId, redKnight2);

            Piece redRook2 = new Piece(PieceType.ROOK, this, 63, 3);
            redRook2.X = 11;
            redRook2.Y = 9;
            PieceGrid[redRook2.X, redRook2.Y] = redRook2;
            PiecesById.Add(redRook2.PieceId, redRook2);
        }

        private void initializeBugHouse()
        {
            IsFourWay = false;

            //todo--implement this
        }
    }
}
